package com.affinity.impl;

import java.util.*;

import com.affinity.*;

/**
 * fixme: Class AbstractPosixAffinityService is for porn
 *
 * @author cheremin
 * @since 27.04.12,  19:06
 */
public class BaseThreadAffinityService implements ThreadAffinityService {
    private final CPULayoutService layoutService;
    private final AffinityMaskSpi affinityMaskSpi;

    private final boolean actuallyAvailable;

    public BaseThreadAffinityService( final CPULayoutService layoutService,
                                      final AffinityMaskSpi affinityMaskSpi ) {
        if ( layoutService == null ) {
            throw new IllegalArgumentException( "'layoutService' can't be null" );
        }
        if ( affinityMaskSpi == null ) {
            throw new IllegalArgumentException( "'affinityMaskSpi' can't be null" );
        }
        this.layoutService = layoutService;
        this.affinityMaskSpi = affinityMaskSpi;

        boolean actuallyAvailable;
        try {
            affinityMaskSpi.pingSpi();
            actuallyAvailable = true;
        } catch ( Throwable e ) {
            actuallyAvailable = false;
        }
        this.actuallyAvailable = actuallyAvailable;
    }


    @Override
    public boolean isActuallyAvailable() {
        return actuallyAvailable;
    }

    @Override
    public void restrictCurrentThreadTo( final CPU... cpus ) {
        restrictCurrentThreadTo( Arrays.asList( cpus ) );
    }

    @Override
    public void restrictCurrentThreadTo( final Iterable<? extends CPU> cpus ) {
        final BitSet affinityMask = new BitSet();
        for ( final CPU cpu : cpus ) {
            affinityMask.set( cpu.id() );
        }

        if ( affinityMask.isEmpty() ) {
            throw new IllegalArgumentException( "can't restrict thread to none cpus (cpus can't be empty)" );
        }

        affinityMaskSpi.setAffinityMask( affinityMask );
    }

    @Override
    public Iterable<? extends CPU> currentThreadRestrictions() {
        final BitSet affinityMask = affinityMaskSpi.getAffinityMask();

        final List<CPU> cpus = new ArrayList<CPU>( affinityMask.cardinality() );
        for ( int cpuNo = 0; cpuNo < affinityMask.length(); cpuNo++ ) {
            if ( affinityMask.get( cpuNo ) ) {
                final CPU cpu = layoutService.cpu( cpuNo );
                cpus.add( cpu );
            }
        }
        return cpus;
    }

    @Override
    public CPU currentThreadCPU() {
        final int cpuNo = affinityMaskSpi.getCurrentThreadCPU();
        return layoutService.cpu( cpuNo );
    }

}
